﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace Chess
{
    public class Move
    {
        private readonly IReadOnlyTile from;
        private readonly IReadOnlyTile to;
        private readonly PieceType promotionType;
        private readonly IEnumerable<IReadOnlyTile> mutatedTiles;

        public Move(IReadOnlyBoard board, IReadOnlyTile from, IReadOnlyTile to, PieceType promotionType = PieceType.Queen)
        {
            this.from = new Tile(from);
            this.to = new Tile(to);
            this.promotionType = promotionType;
            // "ToList" is called here as "TileMutations" is dependent on board state and modification of the board during a later iteration could introduce errors.
            mutatedTiles = TileMutations(board).ToList();
        }

        public IReadOnlyTile From { get { return from; } }
        public IReadOnlyTile To { get { return to; } }
        public PieceType PromotionType { get { return promotionType; } }
        public IEnumerable<IReadOnlyTile> MutatedTiles { get { return mutatedTiles; } }

        private IEnumerable<IReadOnlyTile> TileMutations(IReadOnlyBoard board)
        {
            // Special cases are needed to handle "castling" and "en passant" as they are the only moves which also affect pieces on other tiles.
            int columnDifference = To.Position.Column - From.Position.Column;
            bool castling = From.Piece.PieceType == PieceType.King && Math.Abs(columnDifference) == 2;
            if (castling)
            {
                Column castleColumn = Math.Sign(columnDifference) == 1 ? Column.H : Column.A;
                IReadOnlyTile castleTile = board.GetTile(castleColumn, From.Position.Row);
                Tile castleTileMutation = new Tile(castleTile) { Piece = null };
                yield return castleTileMutation;
                Column castleDestinationColumn = Math.Sign(columnDifference) == 1 ? Column.F : Column.D;
                Tile castleDestinationTileMutation = new Tile(board.GetTile(castleDestinationColumn, From.Position.Row)) { Piece = castleTile.Piece };
                yield return castleDestinationTileMutation;
            }
            bool enPassant = From.Piece.PieceType == PieceType.Pawn && Math.Abs(columnDifference) == 1 && To.Piece == null;
            if (enPassant)
            {
                Tile capturedPawnTileMutation = new Tile(board.GetTile(To.Position.Column, From.Position.Row)) { Piece = null };
                yield return capturedPawnTileMutation;
            }
            Tile toTileMutation = new Tile(To);
            Row promotionRow = From.Piece.PieceColor == PieceColor.White ? Row.Eight : Row.One;
            bool promotion = From.Piece.PieceType == PieceType.Pawn && To.Position.Row == promotionRow;
            if (promotion)
            {
                toTileMutation.Piece = new Piece(From.Piece.PieceColor, PromotionType);
            }
            else
            {
                toTileMutation.Piece = From.Piece;
            }
            yield return toTileMutation;
            yield return new Tile(From) { Piece = null };
        }
    }
}
